package net.amsoft.iservice.isso.webapp.servlet;

import java.io.IOException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * @author Gabe
 * 
 * This is a servlet which is used to perform two-way decryption of greeting 
 * to be shown on Password page displayed to user for OpenID authentication. 
 * 
 * This servlet is to be used till the time 'PBEWithMD5andTripleDES' algorithm 
 * is used for two-way decryption of greeting, since a decryption algorithm 
 * for such encryption is not available in Python. 
 */
public class Decrypt extends HttpServlet 
{
	
	private static Logger oLogger = Logger.getLogger(Decrypt.class);
	private static final long serialVersionUID = 1L;
	static Cipher ecipher;
	static Cipher dcipher; 
    private static byte[] salt = {(byte) 0xA9, (byte) 0x9B, (byte) 0xC8,
			(byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03};
	private static int iterationCount = 19;


    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException
    {
        String pwd=req.getParameter("pwd");
        String clear=req.getParameter("clear");
        String crypt=req.getParameter("crypt");
        String result;
        if (((clear==null) || (clear.equals(""))) && (crypt != null))
        {
            result=getTwoWayDecValue(crypt, pwd);
            resp.setStatus(HttpServletResponse.SC_OK);
            resp.setContentType("text/plain");
            resp.getWriter().print(result);
        }
        else if ((clear!=null) && ((crypt == null) || (crypt.equals(""))))
        {
            result=getTwoWayEncValue(clear,pwd);
            resp.setStatus(HttpServletResponse.SC_OK);
            resp.setContentType("text/plain");
            resp.getWriter().print(result);
        }
        else
        {
            resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Bad parameters");
        }
        
    }
    
    public static String getTwoWayEncValue(String sStringToEnc, String sPassKey) {
    	oLogger.info("getTwoWayEncValue() : Entry : String to decode:"+sStringToEnc);
		try {
			// Create the key
			KeySpec keySpec = new PBEKeySpec(sPassKey.toCharArray(), salt,
					iterationCount);
			SecretKey key = SecretKeyFactory.getInstance(
					"PBEWithMD5AndTripleDES").generateSecret(keySpec);
            		ecipher = Cipher.getInstance(key.getAlgorithm());
			dcipher = Cipher.getInstance(key.getAlgorithm());
			// Pre pare the parameter to the ciphers
			AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
					iterationCount);
			// Create the ciphers
			ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
			dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
			// Encode the string into bytes using utf-8
			byte[] utf8 = sStringToEnc.getBytes("UTF8");
			// Encrypt
			byte[] enc = ecipher.doFinal(utf8);
        		// Encode bytes to base64 to get a string
			String sEncodedStr = new BASE64Encoder().encode(enc).replaceAll(
					"/", "-").replaceAll("\\+", "*");
			if (sEncodedStr.charAt(sEncodedStr.length() - 2) == '=')
				sEncodedStr = sEncodedStr
						.substring(0, sEncodedStr.length() - 2).concat("2");
			else if (sEncodedStr.charAt(sEncodedStr.length() - 1) == '=')
				sEncodedStr = sEncodedStr
						.substring(0, sEncodedStr.length() - 1).concat("1");
			else
				sEncodedStr = sEncodedStr.concat("0");
			return (sEncodedStr);
		} catch (Exception e) {
			oLogger.warn("getTwoWayEncValue() : Exception : "+e.getMessage());
			return null;
		}
	}
    
    public static String getTwoWayDecValue(String sStrToDec, String sPassKey) {
    	oLogger.info("getTwoWayDecValue() : Entry");
		try {
			String sDecodedStr = sStrToDec.replaceAll("-", "/").replaceAll(
					"\\*", "+");
			switch (sDecodedStr.charAt(sDecodedStr.length() - 1)) {
				case '0' :
					sDecodedStr = sDecodedStr.substring(0,
							sDecodedStr.length() - 1);
					break;
				case '1' :
					sDecodedStr = sDecodedStr.substring(0,
							sDecodedStr.length() - 1).concat("=");
					break;
				case '2' :
					sDecodedStr = sDecodedStr.substring(0,
							sDecodedStr.length() - 1).concat("==");
					break;
			}
			// Create the key
			KeySpec keySpec = new PBEKeySpec(sPassKey.toCharArray(), salt,
					iterationCount);
			SecretKey key = SecretKeyFactory.getInstance(
					"PBEWithMD5AndTripleDES").generateSecret(keySpec);
			ecipher = Cipher.getInstance(key.getAlgorithm());
			dcipher = Cipher.getInstance(key.getAlgorithm());
			// Pre pare the parameter to the ciphers
			AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
					iterationCount);
			// Create the ciphers
			ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
			dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
			// Decode base64 to get bytes
			byte[] dec = new BASE64Decoder().decodeBuffer(sDecodedStr);
			// Decrypt
			byte[] utf8 = dcipher.doFinal(dec);
			// Decode using utf-8
			return new String(utf8, "UTF8");
		} catch (Exception e) {
			oLogger.warn("getTwoWayDecValue() : Exception : "+e.getMessage());
			return null;
		}
	}
    
    public static void main(String [] argv)
    {
        String txt="foosdfasessbadooh baz  fha sjkdhf askjhdf ksa fdf er ayeaw kjhsad kfhbar";
        String pwd="arghvarg";
        String enc="";
        oLogger.info("Encoding '"+txt+"' with pwd '"+pwd+"':");
        enc=getTwoWayEncValue(txt, pwd);
        oLogger.info(enc);
        oLogger.info(getTwoWayDecValue(enc, pwd));
    }
}